#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "sthread.h"
#include "SimpleStats.h"


void SS_init(SimpleStats *s, int n, int verbose, int infiniteBowl)
{
  int ii;

  smutex_init(&(s->mutex));
  s->n = n;
  s->verbose = verbose;
  s->infiniteBowl = infiniteBowl;
  s->eatCount = (int *)malloc(n * sizeof(int));
  assert(s->eatCount);
  s->eatingNow = (int *)malloc(n * sizeof(int));
  assert(s->eatingNow);
  for(ii = 0; ii < n; ii++){
    s->eatCount[ii] = 0;
    s->eatingNow[ii] = 0;
  }
  s->curSimul = 0;
  s->maxSimul = 0;
  s->totalFill = 0;
  s->totalEat = 0;
  s->lastCheck = time(&(s->lastCheck));
  assert(s->lastCheck != 1);
  s->lastCheckEatTotal = 0;
  s->cleaningSticks = 0;
  s->cleaningPlates = 0;
  s->cleaningBowl = 0;
  s->cleaningCount = 0;
  s->fillingBowl = 0;
}

static int plateLeft(int me, int tot)
{
  if(me == 0){
    return tot-1;
  }
  return me-1;
}

static int plateRight(int me, int tot)
{
  if(me == tot-1){
    return 0;
  }
  return me+1;
}

static void  progress(SimpleStats *s)
{
  time_t now;
  
  now = time(&now);
  assert(now != -1);
  if(difftime(now, s->lastCheck) >= 1){
    printf("Did %d eats in this second\n", s->totalEat - s->lastCheckEatTotal);
    s->lastCheckEatTotal = s->totalEat;
    s->lastCheck = now;
  }
}

void SS_eatingStart(SimpleStats *s, int id)
{
  int l, r;
  smutex_lock(&(s->mutex));
  l = plateLeft(id, s->n);
  r = plateRight(id, s->n);
  assert(!s->eatingNow[l]);
  assert(!s->eatingNow[r]);
  assert(!s->eatingNow[id]);
  s->eatingNow[id] = 1;
  s->curSimul++;
  if(s->curSimul > s->maxSimul){
    s->maxSimul = s->curSimul;
  }
  if(s->verbose){
    printf("Thread %d starts eating\n", id);
  }
  if(!s->infiniteBowl){
    assert(s->totalEat <= s->totalFill);
  }
  assert(!s->cleaningPlates);
  assert(!s->cleaningSticks);
  smutex_unlock(&(s->mutex));
}

void SS_eatingDone(SimpleStats *s, int id)
{
  int l, r;
  smutex_lock(&(s->mutex));
  l = plateLeft(id, s->n);
  r = plateRight(id, s->n);
  assert(!s->eatingNow[l]);
  assert(!s->eatingNow[r]);
  assert(s->eatingNow[id]);
  s->eatingNow[id] = 0;
  s->curSimul--;
  s->eatCount[id]++;
  if(s->verbose){
    printf("Thread %d done eating\n", id);
  }
  s->totalEat++;
  assert(!s->cleaningPlates);
  assert(!s->cleaningSticks);
  if(!s->infiniteBowl){
    assert(s->totalEat <= s->totalFill);
  }

  progress(s);

  smutex_unlock(&(s->mutex));
}

void SS_fillingStart(SimpleStats *s)
{
  smutex_lock(&(s->mutex));
  if(s->verbose){
    printf("Filling starts\n");
  }
  assert(!s->infiniteBowl);
  s->fillingBowl = 1;
  assert(!s->cleaningBowl);
  assert(s->totalEat <= s->totalFill);
  smutex_unlock(&(s->mutex));
}

void SS_fillingDone(SimpleStats *s, int amount)
{
  smutex_lock(&(s->mutex));
  if(s->verbose){
    printf("Filling done\n");
  }
  assert(!s->infiniteBowl);
  s->totalFill += amount;
  s->fillingBowl = 0;
  assert(!s->cleaningBowl);
  progress(s);
  smutex_unlock(&(s->mutex));
}

void SS_cleaningStart(SimpleStats *s)
{
  smutex_lock(&(s->mutex));
  if(s->verbose){
    printf("Cleaning start\n");
  }
  s->cleaningSticks = 1;
  s->cleaningPlates = 1;
  s->cleaningBowl = 1;
  assert(s->curSimul == 0);
  assert(s->fillingBowl == 0);
  progress(s);
  smutex_unlock(&(s->mutex));
}

void SS_cleaningDoneBowl(SimpleStats *s)
{
  smutex_lock(&(s->mutex));
  if(s->verbose){
    printf("Cleaning done bowl\n");
  }
  assert(s->curSimul == 0);
  assert(s->fillingBowl == 0);
  s->cleaningBowl = 0;
  progress(s);
  smutex_unlock(&(s->mutex));
}

void SS_cleaningDone(SimpleStats *s)
{
  smutex_lock(&(s->mutex));
  if(s->verbose){
    printf("Cleaning done\n");
  }
  s->cleaningSticks = 0;
  s->cleaningPlates = 0;
  s->cleaningBowl = 0;
  s->cleaningCount++;
  assert(s->curSimul == 0);
  progress(s);
  smutex_unlock(&(s->mutex));
}


 void SS_progress(SimpleStats *s)
 {
  smutex_lock(&(s->mutex));
  progress(s);
  smutex_unlock(&(s->mutex));
 }

void SS_printStats(SimpleStats *s)
{
  int ii;
  smutex_lock(&(s->mutex));
  printf("SS: nowEating: %d maxEating: %d ",
         s->curSimul, s->maxSimul);
  printf(" total filled/eaten: %d/%d ", s->totalFill, s->totalEat);
  printf(" total cleanings: %d ", s->cleaningCount);
  printf(" meals completed: ");
  for(ii = 0; ii < s->n; ii++){
    printf("(%d %d) ", ii, s->eatCount[ii]);
  }
  if(!s->infiniteBowl){
    assert(s->totalEat <= s->totalFill);
  }
  printf("\n");
  smutex_unlock(&(s->mutex));
}
